#ifndef MYVECTOR_H
#define MYVECTOR_H

#include <iostream>

/**
 * This class extends the limits of an template array
 */
template <class T>
class myVector{
private:
	int size,capacity;
	T* data;
public:
	/**
	 * implementing iterator and const_iterator
	 */
	typedef T* iterator;
	typedef const T* const_iterator;

	/**
	 * This function gets the first element for the iteration process
	 * \return First element in the array
	 * \remarks Complexity of O(1)
	 */
	iterator begin() { return data;}
	const_iterator begin() const { return data;}

	/**
	 * This function gets the last (+1) element for the iteration process
	 * \return First element in the array
	 * \remarks Comlexity of O(1)
	 */
	iterator end() { return data+size; }
	const_iterator end() const { return data+size; }

	/**
	 * Default constructor: set private variables to zero, default values
	 */
	myVector()
		:size(0),capacity(0),data(0)
	{}

	/**
	 * Constructor: sets the array to a certain size and values
	 * \param The wanted size of the new array
	 * \param The default value of the array
	 * \remarks Complexity is O(size) ~~ Linear complexity
	 */
	myVector(int size, T value)
		: size(size),capacity(size),data(new T[size])
	{
		for(int i=0; i<size;i++)
		{
			this->data[i] = value;
		}
	}

	/**
	 * Constructor: sets the array to a certain capacity.
	 *              array is effictively empty.
	 * \param The wanted capacity of the new array
	 * \remarks Complexity is O(capacity) ~~ Linear complexity
	 */
	myVector(int capacity)
		: size(0),capacity(capacity),data(new T[capacity])
	{}

	/**
	 * Copy constructor, create a new array from a given initialized array
	 * \param An already initialized array
	 * \remarks Complextity is O(source.size) ~~ Linear complexity
	 */
	myVector(const myVector& source)
	{
		T* newData = new T[source.size];

		for (int i=0;i<source.size;i++)
		{
			newData[i] = source.data[i];
		}
		this->size = source.size;
		this->capacity = source.capacity;

		delete[] this->data;
		this->data = newData;
	}

	/**
	 * Destructor, removes all data in the array
	 * \remarks Complexity is O(capacity), calling the destructors of the data
	 *          concluding in Linear Complexity
	 */
	virtual ~myVector()
	{
		delete[] this->data;
	}

	/**
	 * This function gets the actual size of the array
	 * \remarks Complexity is O(1)
	 */
	int Size() const
	{
		return this->size;
	}

	/**
	 * This function tells if the array is empty or not
	 * \remarks Complexity is O(1)
	 */
	int Empty() const
	{
		return (this->size ==0 || this->capacity == 0);
	}

	/**
	 * This function gets the capacity of the array
	 * \remarks Complexity is O(1)
	 */
	int Capacity() const
	{
		return this->capacity;
	}

	/**
	 * This function gets a specific value
	 * \remarks Complexity is O(1)
	 */
	T& operator[](int i)
	{
		return data[i];
	}

	/**
	 * This function gets a specific const value
	 * \remarks Complexity is O(1)
	 */
	const T& operator[](int i) const
	{
		return data[i];
	}

	/**
	 * This assignmet operator creates a copy of a certain array
	 * \param The array object to be copied
	 * \remarks Complexity is O(source.size) ~~ Linear Complexity
	 */
	myVector<T>& operator=(const myVector<T>& source)
	{
		// check for self assignment
		if (&source == this) {
			return *this;
		}

		T* newData = new T[source.size];

		for (int i=0;i<source.size;i++) {
			newData[i] = source.data[i];
		}
		delete[] this->data;
		this->data = newData;

		this->capacity = source.capacity;
		this->size = source.size;
		return *this;
	}

	/**
	 * This function changes the capacity and/or the size of the array
	 * by creating new elements or removing stale elements
	 * \param The wanted capacity of the array
	 * \param An optional parameter which states the new value added.
	 * \remarks Complexity is O(|size-capacity|) ~~ Linear Complexity
	 */
	void resize(int size, T value=T())
	{
		if (size == this->size)
		{ // check the sizes
			return;
		}
		T* newData = new T[size];


		if (size < this->size)
		{       // just copy
			for(int i=0;i<size;i++)
			{
				newData[i] = this->data[i];
			}
		}
		else
		{
			for(int i =0;i<this->size;i++)
			{  // copy and...
				newData[i] = this->data[i];
			}
			for(int i=this->size;i<size;i++)
			{ // ...and insert new ones
				newData[i] = value;
			}
		}

		delete[] this->data;
		this->data = newData;
		this->size = size;
		this->capacity = size;
	}

	/**
	 * This function reserves memory allocations by predetermining the wanted
	 * size of the array, if it is smaller than the current capacity nothing
	 * happens
	 * \param The new capacity to be allocated
	 */
	void reserve(int amount)
	{
		if (amount <= this->capacity)
		{ // check if smaller
			return;
		}
		else
		{
			T* newData = new T[amount];

			for(int i=0;i<this->size;i++)
			{
				newData[i] = this->data[i];
			}

			this->capacity = amount;
			delete[] this->data;
			this->data = newData;
		}
	}

	/**
	 * This function gets the first element of the array
	 * \remarks Complexity is O(1)
	 */
	T& front()
	{
		return this->data[0];
	}

	/**
	 * This function gets the last element of the array
	 * \remarks Complexity is O(1)
	 */
	T& back()
	{
		return this->data[this->size-1];
	}

	/**
	 * This function inserts a value into the array
	 * \param A const value to be inserted
	 * \remarks
	 */
	void push_back(const T& value)
	{
		if (this->size < this->capacity)
		{
			this->data[this->size] = value;
			this->size++;
		}
		else
		{
			T* newData = new T[this->size+1];

			for(int i=0;i<this->size;i++)
			{
				newData[i] = this->data[i];
			}
			newData[size] = value;

			this->size++;
			this->capacity++;
			delete[] this->data;
			this->data = newData;
		}
	}

	/**
	 * This function removes the last inserted element from the array
	 * \remarks Complexity is O(1)
	 */
	void pop_back()
	{
		this->size--;
	}

	/**
	 * This function clears the array
	 * \remarks Complexity is O(1)
	 */
	void clear()
	{
		this->size = 0;
	}


};

#endif // MYVECTOR_H
